#!/usr/bin/env python2

import os
import sys
import time
import errno
import getopt

from config import Config
from utils import Exp, _dwarn, _derror, _str2hosts, _exec_pipe
from storage import Storage
from nodepool import NodePool

from metanode_balance_with_diskmap import balance as metanode_balance

class ClusterMeta():
    def __init__(self):
        self.config = Config()
        self.tmp_script = '/tmp/start_script'
        admin = os.path.join(self.config.lich, 'admin')
        self.node_script = os.path.join(admin, 'node.py')
        self.lich_admin = os.path.join(self.config.lich, "libexec/lich.admin")
        self.meta = None
        self.storage = None
        self.admin = None
        self.hosts_nohosts = None

    def host2instence(self, hosts):
        instences = []
        for (k, v) in hosts.items():
            if (v == 1):
                instences.append(k)
            else:
                for i in range(0, v):
                    if (i == 0):
                        instences.append(k)
                    else:
                        instences.append("%s/%d" %(k, i))

        return instences

    def _createcluster(self, hosts):
        try:
            instences = self.host2instence(hosts)
            _exec_pipe([self.config.lich + "/libexec/lich.admin",  "--createcluster", instences[0]])
            instences.pop(0)
            _exec_pipe([self.config.lich + "/libexec/lich.admin",  "--addnode"] + instences)
        except Exp, e:
            _derror(e.err)
            exit(e.errno)

    def _createcluster1(self, instences):
        for i in range(len(instences)):
            host = instences[i]
            if host == self.config.hostname:
                try:
                    _exec_pipe([self.config.lich + "/libexec/lich.admin",  "--createcluster", instences[i]])
                    instences.pop(i)
                    break
                except Exp, e:
                    _derror(e.err)
                    exit(e.errno)

        self._addnode1(instences)

    def __loadmeta(self):
        if (self.admin != None):
            return

        if (self.storage == None):
            self.storage = Storage(self.config)

        self.pool = NodePool(self.storage)
        self.admin = self.pool.admin
        self.meta = self.pool.metas

    def __ismeta(self, host):
        self.__loadmeta()

        if (host in self.meta):
            return True
        else:
            return False

    def _checkmeta(self, newlen):
        if (self.storage == None):
            self.storage = Storage(self.config)

        retry = 0
        while (retry < 10):
            lst = []
            try:
                lst = self.storage.list_node(True)
            except Exp, e:
                if (e.errno ==  errno.EAGAIN):
                    time.sleep(1)
                    continue

            if (len(lst) < newlen):
                print "list node:%s need:%s retry:%s" % (lst, newlen, retry)
                time.sleep(1)
                retry = retry + 1
            else:
                break

        if len(lst) == 0:
            raise Exp(errno.EPERM, "No node found")

        metanode_balance(lst)

    def _addnode(self, hosts):
        newlist = _str2hosts(hosts)
        newlist1 = {}
        for i in newlist:
            newlist1[i] = 1
        instences = self.host2instence(newlist1)
        try:
            for i in instences:
                _exec_pipe([self.config.lich + "/libexec/lich.admin", "--addnode", i])
        except Exp, e:
            _derror(e.err)
            exit(e.errno)

    def _addnode1(self, instences):
        for i in instences:
            self.__addnode1(i)

    def __addnode1(self, i):
        j = 0
        while True:
            try:
                _exec_pipe([self.config.lich + "/libexec/lich.admin", "--addnode", i])
            except Exp, e:
                if (e.errno in [errno.EBUSY, errno.EAGAIN, errno.ETIMEDOUT, errno.ENONET, errno.ENOSYS]) and (j < 10):
                    j = j + 1
                    _dwarn("addnode retry: count: %s, ret: %s" % (j, os.strerror(e.errno)))
                    time.sleep(3)
                    continue
                _derror(e.err)
                exit(e.errno)
            break


def usage():
    print ("usage:")
    print ("hosts example: dc100[0-3].rack[2-4].host[3-6,7-9] dc101[0-3].rack[2-4].host[3-6,7-9]")
    print (sys.argv[0] + " --init")
    print (sys.argv[0] + " --addnode <hosts>")
    print (sys.argv[0] + " --listnode")

def main():
    try:
        opts, args = getopt.getopt(
                sys.argv[1:],
                'h', ['help', 'host', 'init', 'addnode', 'listnode', 'health']
                )
    except getopt.GetoptError, err:
        print str(err)
        usage()
        exit(errno.EINVAL)

    cluster = ClusterMeta()
    for o, a in opts:
        if o in ('--help'):
            usage()
            exit(0)
        elif o == '--init':
            hosts = {}
            hosts[cluster.config.hostname] = 1
            
            cluster._createcluster(hosts)
        elif o == '--addnode':
            oldlist = cluster.storage.list_node()
            hosts = cluster.config.list2dict(sys.argv[2:])
            cluster._addnode(hosts)
            cluster._checkmeta(len(hosts), len(oldlist))
        elif o == '--listnode':
            storage = Storage(Config())
            list1 = storage.list_node(True)
            for (k, v) in list1.items():
                print("%s:%s" % (k, v))

        elif o == '--health':
            cluster.health()
        else:
            assert False, 'oops, unhandled option: %s, -h for help' % o
            exit(1)

if __name__ == '__main__':
    if (len(sys.argv) == 1):
        usage()
    else:
        main()
